package com.ruicar.afs.cloud.gateway.filter;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.ruicar.afs.cloud.common.core.constant.CommonConstants;
import com.ruicar.afs.cloud.common.core.log.util.LogUtils;
import com.ruicar.afs.cloud.common.core.util.IResponse;
import com.ruicar.afs.cloud.common.core.util.IpUtils;
import com.ruicar.afs.cloud.common.gateway.filters.AfsFilterHelper;
import com.ruicar.afs.cloud.gateway.config.FilterIgnorePropertiesConfig;
import com.ruicar.afs.cloud.gateway.util.AfsGateWayHelper;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.ruicar.afs.cloud.common.core.constant.CommonConstants.AFS_TRACKER_ID;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;

/**
 * @author Fzero
 * @date 2019-01-21
 * <p>
 * 全局拦截器，作用所有的微服务
 * <p>
 * 1. 对请求头中参数进行处理 from 参数进行清洗
 * 2. 重写StripPrefix = 1,支持全局
 * <p>
 */
@Component
@Slf4j
@AllArgsConstructor
public class RequestGlobalFilter implements GlobalFilter, Ordered {
	private FilterIgnorePropertiesConfig filterIgnorePropertiesConfig;
	private static final  AntPathMatcher pathMatcher = new AntPathMatcher();
	private final Environment environment;
	/**
	 * Process the Web request and (optionally) delegate to the next
	 * {@code WebFilter} through the given {@link GatewayFilterChain}.
	 *
	 * @param exchange the current server exchange
	 * @param chain    provides a way to delegate to the next filter
	 * @return {@code Mono<Void>} to indicate when request processing is complete
	 */
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		String trackerId = AfsGateWayHelper.getHeaderValue(exchange,CommonConstants.AFS_TRACKER_ID);
		MDC.put(AFS_TRACKER_ID,StringUtils.isEmpty(trackerId)?UUID.fastUUID().toString().replaceAll("-",""):trackerId);
		LogUtils.setSystemName(environment);
		// 1. 清洗请求头中from 参数
		ServerHttpRequest request = exchange.getRequest().mutate()
				.headers(httpHeaders -> {
				})
				.build();
		// 2. 重写StripPrefix
		addOriginalRequestUrl(exchange, request.getURI());
		String rawPath = request.getURI().getRawPath();
		String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/")).skip(1L).collect(Collectors.joining("/"));
		ServerHttpRequest.Builder builder = null;
		if (filterIgnorePropertiesConfig.getUris().stream().filter(path->pathMatcher.match(path,newPath)).count()<1) {
			String access_token = AfsGateWayHelper.getHeaderValue(exchange,CommonConstants.ACCESS_TOKEN);
			String tenant_id = AfsGateWayHelper.getHeaderValue(exchange,CommonConstants.TENANT_ID);
			if (StrUtil.isEmpty(access_token)) {
				List<Map.Entry<String,String>> afsMatches = AfsFilterHelper.getPatternNameSet().stream().filter(pattern->pathMatcher.match(pattern.getKey(),rawPath)).collect(Collectors.toList());
				if(afsMatches.size()<1){
					ServerHttpResponse response = exchange.getResponse();
					response.setStatusCode(HttpStatus.BAD_REQUEST);
					response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
					return response.writeWith(Mono.just(response.bufferFactory()
							.wrap(JSON.toJSONBytes(
									IResponse.fail("token error")))));
				}else if(afsMatches.size()>1){
					ServerHttpResponse response = exchange.getResponse();
					response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
					response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
					log.error("[{}] 匹配到多个过滤器，服务端配置错误[{}]",newPath,afsMatches.stream().map(Map.Entry::getValue).collect(Collectors.joining("|")));
					return response.writeWith(Mono.just(response.bufferFactory()
							.wrap(JSON.toJSONBytes(IResponse.fail("service config error")))));
				}else {
					try {
						builder = AfsFilterHelper.getAfsFilter(afsMatches.get(0).getValue()).processRequest(exchange);
					}catch (RuntimeException e){
						 log.error("重写URI失败",e);
						return  exchange.getResponse().writeWith(Mono.just( exchange.getResponse().bufferFactory()
								.wrap(JSON.toJSONBytes(IResponse.fail(e.getMessage())))));
					}
				}
			}else{
				builder = request.mutate().path(newPath);
				builder.header(CommonConstants.AUTHORIZATION, CommonConstants.BEARER + access_token);
				builder.header(CommonConstants.TENANT_ID, tenant_id);
				builder.header(CommonConstants.ACCESS_TOKEN, access_token);
				builder.header(CommonConstants.REAL_REQUEST_IP,IpUtils.getClientIp(request));
			}
		} else {
			builder = request.mutate().path(newPath);
		}
		builder.header(AFS_TRACKER_ID, MDC.get(AFS_TRACKER_ID));
		ServerHttpRequest newRequest = builder.build();
		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
		return chain.filter(exchange.mutate().request(newRequest.mutate().build()).build());
	}


	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE+2;
	}
}
